'''Functions for working with author names as found in BibTeX.

# TO DO: Add mini manual here.

@copyright: (c) 2004-2009, Jim Pitman, Dept. Statistics, U.C. Berkeley
    <http://www.stat.berkeley.edu/users/pitman>
@copyright: (c) 2009-2010, Benjamin Kalish

@license:
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.
'''

import string
import sys

from simpleparse.error import ParserSyntaxError

from bkn.bibtex._bibtex_string_parser import *
from bkn.bibtex.string_tools import blank
from bkn.bibtex.utilities import assert_objects_are_unicode
from bkn.bibtex.utilities import ensure_objects_are_unicode
from bkn.bibtex.error import NameError

__docformat__ = 'epytext'

def first_von_last_jr(namestring):
    '''Splits a name into first, von, last, and jr parts according to
    the rules of BibTeX.
    
    Usage:
    
        >>> (first, von, last, jr) = first_von_last_jr(namestring)
    
    Examples:
    
        >>> first_von_last_jr("King, Jr., Martin Luther")
        ('Martin Luther', '', 'King', 'Jr.')
    
    '''
    ensure_objects_are_unicode(namestring)
    try:
        result = BIBTEX_STRING_PARSER.parse(
            namestring,
            production='name',
            processor=BibtexNameProcessor()
            )
        assert_objects_are_unicode(*result)
        return result
    except ParserSyntaxError as err:
        message = 'Could not parse the name "' + namestring + '".' + str(err)
        raise NameError(message)    

def first_last(namestring):
    '''Splits a name into its first and last parts according to the
    rules of BibTeX.
    
    Usage:
    
        >>> (firstname, lastname) = first_last(namestring)
    
    Examples:
        >>> bibtex.first_last('Cleese, John')
        ('John', 'Cleese')
    '''    
    ensure_objects_are_unicode(namestring)
    (first, von, last, suffix) = first_von_last_jr(namestring)
    result = (first, last)
    assert_objects_are_unicode(*result)
    return result
    
def first_last_suffix(namestring):
    '''Splits a name into first, last, and suffix parts according to the
    rules of BibTeX.

    Usage: (firstname, lastname, suffix) = first_last_suffix(namestring)
    '''
    ensure_objects_are_unicode(namestring)
    (first, von, last, suffix) = first_von_last_jr(namestring)
    result = (first,last,suffix)
    assert_objects_are_unicode(*result)
    return result

def last_name(namestring):
    '''Returns the last name part of a name according to the rules of
    BibTeX.
    '''
    ensure_objects_are_unicode(namestring)
    (first, last,suffix) = first_last_suffix(namestring)
    result = last 
    assert_objects_are_unicode(result)
    return result

def first_name(namestring):
    '''Returns the first name part of a name according to the rules of
    BibTeX.'''
    ensure_objects_are_unicode(namestring)
    (first, last, suffix) = first_last_suffix(namestring)
    result = first 
    assert_objects_are_unicode(result)
    return result

def first_initial(namestring):
    '''Returns the first inital of a name according to the rules of
    BibTeX.

    Returns None if the name has no first initial.
    '''
    ensure_objects_are_unicode(namestring)
    initial_list = initials(namestring)
    if len(initial_list) > 0:
        result = initial_list[0]
        assert_objects_are_unicode(result)
        return initial_list[0]
    else:
        return None

def initials(namestring):
    '''Returns a list composed of the initials of a first name,
    according to the rules of BibTex.

    @attention: This function does not return the initial of the last
    name!
    '''
    ensure_objects_are_unicode(namestring)
    (first, last) = first_last(namestring)
    result = []
    last_char = ''
    chars = BIBTEX_STRING_PARSER.parse(
        first,
        production='string_to_split',
        processor=StringSplitter()
        )
    for char in chars:
        if last_char in ['-','',' ']: result.append(last_char.strip()+char)
        last_char = char
    assert_objects_are_unicode(*result)
    return result
    
    
def last_name_first(namestring):
    '''Writes a name in inverted form (last name first).
        
    Examples
            >>> bibtex.last_name_first('Martin Luther King, Jr.')
            'King, Martin Luther, Jr.'
           
            >>> bibtex.last_name_first('Cleese, John')
            'Cleese, John'
    
    Note that this function places suffixes last in the list, while
    BibTeX expects them to be in the middle, so
    
        >>> bibtex.last_name_first('Last, Jr., First')
        'Last, First, Jr.'

    '''
    ensure_objects_are_unicode(namestring)
    (first, von, last, jr) = first_von_last_jr(namestring)
    list = []
    if not blank(von): list.append(von)
    if not blank(last): list.append(last)
    list = [' '.join(list)]
    if not blank(first): list.append(first)
    if not blank(jr): list.append(jr)
    return ', '.join(list)
    
def first_name_first(namestring):
    '''Writes a name in univerted form (first name first)'''
    ensure_objects_are_unicode(namestring)
    (first, von, last, jr) = first_von_last_jr(namestring)
    list = []
    if not blank(first): list.append(first)
    if not blank(von): list.append(von)
    if not blank(last): list.append(last)
    n = ' '.join(list)
    if not blank(jr): n += ', ' + jr
    return n

def author_ls(authorstring):
    '''Converts a BibTeX author string into a list of author names.'''
    ensure_objects_are_unicode(authorstring)
    try:
        result = BIBTEX_STRING_PARSER.parse(
            authorstring,
            production='author_list',
            processor=NameListSplitter()
            )
    except ParserSyntaxError as err:
        message = 'Could not parse the names in"' + authorstring + '".' + str(err)
        raise NameError(message)
    return result
